/*
     llmath.c
     Copyright (c) 2007 Helmar Wodtke
     License: MIT
     Purpose: provide "long long" math
     
     Note: You've to implement this in a different way if your compiler (as
     ACK) does not provide support for "long long" types.
*/

#include "4p.h"

typedef long long dcell;
typedef unsigned long long udcell;
extern cell tos;
extern cell *sp;

/*
   m*          : _m_mul();
   um*         : _um_mul();
   um/mod      : _um_mod();
   sm/rem      : _sm_rem();
   fm/mod      : _fm_mod();
   ud/mod      : _ud_mod();
   m+          : _m_add();
   um+         : _um_add();
   md*         : _md_mul();
   dabs        : _dabs();
*/

void
_m_mul (void)
{
  dcell r = (dcell) tos * *sp;
  *sp = (cell) r;
  tos = (cell) (r >> CELLBITS);
  return;
}

void
_um_mul (void)
{
  udcell r = (udcell) (ucell) tos * (ucell) * sp;
  *sp = (cell) r;
  tos = (cell) (r >> CELLBITS);
  return;
}

void
_um_mod (void)
{
  udcell r = (((udcell) * sp) << CELLBITS) + (ucell) * (sp - 1);
  *--sp = (cell) (r % (ucell) tos);
  tos = (cell) (r / (ucell) tos);
  return;
}

void
_md_mul (void)
{
  dcell r = (((dcell) * sp) << CELLBITS) + (ucell) * (sp - 1);
  r *= tos;
  *--sp = (cell) r;
  tos = (cell) (r >> CELLBITS);
  return;
}

void
_m_add (void)
{
  dcell r = (((dcell) * sp) << CELLBITS) + (ucell) * (sp - 1) + tos;
  *--sp = (cell) r;
  tos = (cell) (r >> CELLBITS);
  return;
}

void
_um_add (void)
{
  udcell r = (((udcell) * sp) << CELLBITS) + (ucell) * (sp - 1) + (ucell) tos;
  *--sp = (ucell) r;
  tos = (ucell) (r >> CELLBITS);
  return;
}

void
_sm_rem (void)			/* assuming C uses symmetric division */
{
  dcell r = (((dcell) * sp) << CELLBITS) + (ucell) * (sp - 1);
  *--sp = (cell) (r % tos);
  tos = (cell) (r / tos);
  return;
}

void
_fm_mod (void)			/* floored division */
{
  cell tm = tos;
  dcell r = (((dcell) * sp) << CELLBITS) + (ucell) * (sp - 1);
  *--sp = (cell) (r % tos);
  tos = (cell) (r / tos);
  if (*sp && tos < 0)
    {
      tos--;
      *sp += tm;
    }
  return;
}

void
_ud_mod (void)
{
  udcell r = (((udcell) * sp) << CELLBITS) + (ucell) * (sp - 1);
  ucell tm = (ucell) tos;
  tos = r % tm;
  r /= tm;
  *sp = (cell) (r >> CELLBITS);
  *(sp - 1) = (cell) r;
  return;
}

void
_dabs (void)
{
  dcell r = (((dcell) tos) << CELLBITS) + (ucell) * sp;
  if (r < 0)
    {
      r = -r;
      *sp = (ucell) r;
      tos = (cell) (r >> CELLBITS);
    }
  return;
}
